﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

namespace MassIntellect.WorkIt.Frames
{
    /// <summary>
    /// Подставляет заданное значение в параметры сигнала
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Changer<T> : WorkFrame
    {
        /// <summary>
        /// Способ обработки ключей
        /// </summary>
        [Serialize]
        public ChangeVariant ChangingSet { get; set; }

        /// <summary>
        /// Блокировать сигнал, если замены не было
        /// </summary>
        [Serialize]
        public bool IsBlockIfNotChanged { get; set; }

        private readonly Dictionary<string, T> values = new Dictionary<string, T>();

        protected override void protectedPulse(PulseArgs args)
        {
            PulseArgs a2 = args;
            IEnumerable<string> intr;
            switch (ChangingSet)
            {
                case ChangeVariant.AddKey:
                    intr = values.Keys.Except(args.Keys);
                    break;
                case ChangeVariant.ResetValue:
                    intr = args.Keys.Intersect(values.Keys);
                    break;
                case ChangeVariant.FullChange:
                    intr = values.Keys;
                    break;
                default:
                    intr = new string[0];
                    break;
            }
            bool allow = intr.Any();
            if (allow)
            {
                a2 = args.Clone();
                foreach (var item in intr)
                    a2[item] = values[item];
            }
            if (allow || !IsBlockIfNotChanged)
                base.protectedPulse(a2);
        }

        public override void SetUp(XElement xe)
        {
            base.SetUp(xe);
            var q = xe.Elements("Values").Elements("Value");

            if (typeof(ISerialize).IsAssignableFrom(typeof(T)))
                values.AddRange(q
                    .Select(s => new KeyValuePair<string, T>((string)s.Attribute("key")
                        , (T)XmlSerializeProvider.Deserialize<ISerialize>(s.Elements().FirstOrDefault()))));
            else
                values.AddRange(q
                    .Select(s => new KeyValuePair<string, T>((string)s.Attribute("key")
                        , XmlSerializeProvider.DeserializeValue<T>(s))));
        }

        public override XElement Serialize()
        {
            var xe = base.Serialize();
            if (values.Count > 0)
            {
                ISerialize sr;
                var ser = new Dictionary<string, object>();
                if (typeof(ISerialize).IsAssignableFrom(typeof(T)))
                    ser.AddRange(values
                        .Select(s => new KeyValuePair<string, object>(s.Key,
                            (sr = (ISerialize)s.Value) != null ? (object)sr.Serialize() : s.Value)));
                else
                    ser.AddRange(values
                        .Select(s => new KeyValuePair<string, object>(s.Key, s.Value)));
                xe.Add(new XElement("Values",
                    ser.Select(s => new XElement("Value",
                        new XAttribute("key", s.Key), s.Value))));
            }
            return xe;
        }

        public Changer()
        {
            IsPulseIfDisabled = true;
        }
    }

    /// <summary>
    /// Полный вариант добавления значений
    /// </summary>
    [Flags]
    public enum ChangeVariant
    {
        /// <summary>
        /// Ничего не добавлять
        /// </summary>
        None = 0,
        /// <summary>
        /// Добавлять ключ если отсутвует
        /// </summary>
        AddKey = 1,
        /// <summary>
        /// Изменять значения с присутвующими ключями
        /// </summary>
        ResetValue = 2,
        /// <summary>
        /// Замена и установка значений всех ключей
        /// </summary>
        FullChange = AddKey | ResetValue
    }
}
